package com.hsurosy.hsuchain.nft.order.domain.service;

import cn.hutool.core.lang.Assert;
import com.hsurosy.hsuchain.nft.api.order.constant.OrderErrorCode;
import com.hsurosy.hsuchain.nft.api.order.constant.TradeOrderEvent;
import com.hsurosy.hsuchain.nft.api.order.request.*;
import com.hsurosy.hsuchain.nft.api.order.response.OrderResponse;
import com.hsurosy.hsuchain.nft.api.user.constant.UserType;
import com.hsurosy.hsuchain.nft.base.exception.BizException;
import com.hsurosy.hsuchain.nft.base.exception.RepoErrorCode;
import com.hsurosy.hsuchain.nft.base.utils.BeanValidator;
import com.hsurosy.hsuchain.nft.order.domain.entity.TradeOrder;
import com.hsurosy.hsuchain.nft.order.domain.entity.TradeOrderStream;
import com.hsurosy.hsuchain.nft.order.domain.exception.OrderException;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hsurosy.hsuchain.nft.order.infrastructure.mapper.OrderMapper;
import com.hsurosy.hsuchain.nft.order.infrastructure.mapper.OrderStreamMapper;
import org.apache.shardingsphere.transaction.annotation.ShardingSphereTransactionType;
import org.apache.shardingsphere.transaction.core.TransactionType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionTemplate;

import java.util.function.Consumer;
import java.util.function.Function;

import static com.hsurosy.hsuchain.nft.api.order.constant.OrderErrorCode.ORDER_NOT_EXIST;
import static com.hsurosy.hsuchain.nft.api.order.constant.OrderErrorCode.PERMISSION_DENIED;
import static com.hsurosy.hsuchain.nft.base.response.ResponseCode.SYSTEM_ERROR;
import static java.util.Objects.requireNonNull;

/**
 * 订单管理服务类
 *
 * @Author Hsu琛君珩
 * @Date 2024-10-05 17:49
 * @Description
 * @Version: v1.0.0
 */
@Service
public class OrderManageService extends ServiceImpl<OrderMapper, TradeOrder> {

    private static final Logger logger = LoggerFactory.getLogger(OrderManageService.class);

    @Autowired
    private OrderMapper orderMapper;

    @Autowired
    private OrderStreamMapper orderStreamMapper;

    @Autowired
    protected TransactionTemplate transactionTemplate;

    @Autowired
    protected ApplicationContext applicationContext;

    /**
     * 创建订单
     * <p>
     * 该方法执行订单创建的核心逻辑，并生成订单流水及触发相应的订单创建事件。
     *
     * @param request 订单创建请求
     * @return OrderResponse 订单响应
     */
    @Transactional(rollbackFor = Exception.class)
    public OrderResponse create(OrderCreateRequest request) {
        // 前置校验-幂等查询，检查是否已存在订单
        TradeOrder existOrder = orderMapper.selectByIdentifier(request.getIdentifier(), request.getBuyerId());
        if (existOrder != null) {
            // 如果已存在订单，则返回订单ID
            return new OrderResponse.OrderResponseBuilder().orderId(existOrder.getOrderId()).buildSuccess();
        }

        // 创建订单核心逻辑
        TradeOrder tradeOrder = TradeOrder.createOrder(request);

        // 订单创建
        // 订单流水创建
        // 订单创建事件（可异步处理）

        return new OrderResponse.OrderResponseBuilder().orderId(tradeOrder.getOrderId()).buildSuccess();
    }

    /**
     * 支付订单
     * <p>
     * 处理订单支付逻辑，使用 ShardingSphere 进行分布式事务处理。
     *
     * @param request 订单支付请求
     * @return OrderResponse 订单响应
     */
    @Transactional(rollbackFor = Exception.class)
    @ShardingSphereTransactionType(TransactionType.BASE)
    public OrderResponse pay(OrderPayRequest request) {
        // 支付逻辑尚未实现
        return null;
    }

    /**
     * 确认订单
     * <p>
     * 处理订单确认逻辑。
     *
     * @param request 订单确认请求
     * @return OrderResponse 订单响应
     */
    public OrderResponse confirm(OrderConfirmRequest request) {
        // 确认逻辑尚未实现
        return null;
    }

    /**
     * 取消订单
     * <p>
     * 处理订单取消逻辑。
     *
     * @param request 订单取消请求
     * @return OrderResponse 订单响应
     */
    public OrderResponse cancel(OrderCancelRequest request) {
        // 取消逻辑尚未实现
        return null;
    }

    /**
     * 订单超时处理
     * <p>
     * 处理订单超时逻辑。
     *
     * @param request 订单超时请求
     * @return OrderResponse 订单响应
     */
    public OrderResponse timeout(OrderTimeoutRequest request) {
        // 超时逻辑尚未实现
        return null;
    }

    /**
     * 完成订单
     * <p>
     * 处理订单完成的逻辑。
     *
     * @param request 订单完结请求
     * @return OrderResponse 订单响应
     */
    public OrderResponse finish(OrderFinishRequest request) {
        // 完成逻辑尚未实现
        return null;
    }

    /**
     * 通用订单更新逻辑
     * <p>
     * 该方法封装了订单的更新操作，包含事务处理逻辑。根据订单更新请求执行相应的业务操作。
     *
     * @param orderRequest 更新请求
     * @param consumer     执行订单更新的具体操作
     * @return OrderResponse 订单响应
     */
    protected OrderResponse doExecute(BaseOrderUpdateRequest orderRequest, Consumer<TradeOrder> consumer) {
        OrderResponse response = new OrderResponse();
        return handle(orderRequest, response, "doExecute", request -> {
            TradeOrder existOrder = orderMapper.selectByOrderId(request.getOrderId());
            if (existOrder == null) {
                throw new OrderException(ORDER_NOT_EXIST);
            }

            if (!hasPermission(existOrder, orderRequest.getOrderEvent(), orderRequest.getOperator(), orderRequest.getOperatorType())) {
                throw new OrderException(PERMISSION_DENIED);
            }

            TradeOrderStream existStream = orderStreamMapper.selectByIdentifier(orderRequest.getIdentifier(), orderRequest.getOrderEvent().name(), orderRequest.getOrderId());
            if (existStream != null) {
                return new OrderResponse.OrderResponseBuilder().orderId(existStream.getOrderId()).streamId(existStream.getId().toString()).buildDuplicated();
            }

            // 核心逻辑执行
            consumer.accept(existOrder);

            // 开启事务处理订单更新和订单流水
            return transactionTemplate.execute(transactionStatus -> {
                boolean result = orderMapper.updateByOrderId(existOrder) == 1;
                Assert.isTrue(result, () -> new OrderException(OrderErrorCode.UPDATE_ORDER_FAILED));

                TradeOrderStream orderStream = new TradeOrderStream(existOrder, orderRequest.getOrderEvent(), orderRequest.getIdentifier());
                result = orderStreamMapper.insert(orderStream) == 1;
                Assert.isTrue(result, () -> new BizException(RepoErrorCode.INSERT_FAILED));

                return new OrderResponse.OrderResponseBuilder().orderId(orderStream.getOrderId()).streamId(String.valueOf(orderStream.getId())).buildSuccess();
            });
        });
    }

    /**
     * 校验用户权限
     * <p>
     * 根据订单事件、操作员信息，校验是否有权限执行当前操作。
     *
     * @param existOrder   当前订单
     * @param orderEvent   订单事件
     * @param operator     操作员
     * @param operatorType 操作员类型
     * @return 是否有权限执行操作
     */
    private boolean hasPermission(TradeOrder existOrder, TradeOrderEvent orderEvent, String operator, UserType operatorType) {
        switch (orderEvent) {
            case PAY:
            case CANCEL:
                return existOrder.getBuyerId().equals(operator);
            case TIME_OUT:
            case CONFIRM:
            case FINISH:
                return operatorType == UserType.PLATFORM;
            default:
                throw new UnsupportedOperationException("unsupported order event : " + orderEvent);
        }
    }

    /**
     * 处理订单操作的通用方法
     *
     * @param request   订单操作请求
     * @param response  订单操作响应
     * @param method    方法名称
     * @param function  处理订单操作的具体逻辑
     * @param <T>       请求类型
     * @param <R>       响应类型
     * @return OrderResponse 订单操作的响应
     */
    public static <T, R extends OrderResponse> OrderResponse handle(T request, R response, String method, Function<T, R> function) {
        logger.info("before execute method={}, request={}", method, JSON.toJSONString(request));
        try {
            requireNonNull(request);
            BeanValidator.validateObject(request);
            response = function.apply(request);
        } catch (OrderException e) {
            logger.error(e.toString(), e);
            response.setSuccess(false);
            response.setResponseCode(e.getErrorCode().getCode());
            response.setResponseMessage(e.getErrorCode().getMessage());
            logger.error("failed execute method={}, exception={}", method, JSON.toJSONString(e));
        } catch (Exception e) {
            response.setSuccess(false);
            response.setResponseCode(SYSTEM_ERROR.name());
            response.setResponseMessage(e.getMessage());
            logger.error("failed execute method={}, exception={}", method, JSON.toJSONString(e));
        } finally {
            logger.info("after execute method={}, result={}", method, JSON.toJSONString(response));
        }
        return response;
    }
}
